#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/proc_fs.h>
#include "hello_proc.h"
static char *jihaixuan_proc_buf = NULL;
int jihaixuan_proc_open(struct inode *this_inode, struct file *this_file)
{
    printk(KERN_INFO "hello jihaixuan proc open\n");
    return 0;
};

int jihaixuan_proc_release(struct inode *this_inode, struct file *this_file)
{
    printk(KERN_INFO "goodbye jihaixuan proc close\n");
    return 0;
};
ssize_t jihaixuan_proc_read(struct file *this_file, char __user *user_buf, size_t read_counts, loff_t *this_file_p)
{
    if (printk_ratelimit()){
        printk(KERN_INFO "more limit\n");
    }
    printk("request read pos %lld\n", *this_file_p);
    // 读到末尾了
    if (*this_file_p >= 100)
    {
        return 0;
    }
    // 重定义读取数量
    read_counts = read_counts > (100 - *this_file_p) ? (100 - *this_file_p) : read_counts;
    printk(KERN_INFO "redirect read counts %ld\n", read_counts);

    // 这次全部写入成功 返回成功 否则 返回失败
    int remain = copy_to_user(user_buf, &jihaixuan_proc_buf[*this_file_p], read_counts);
    if (remain)
    {
        return -EFAULT;
    }
    else
    {
        // 更新文件位置偏移
        *this_file_p += read_counts;
        return read_counts;
    }
}
ssize_t jihaixuan_proc_write(struct file *this_file, const char __user *user_buf, size_t write_counts, loff_t *this_file_p)
{
    if (printk_ratelimit()){
        printk(KERN_INFO "more limit\n");
    }
    printk("request write pos %lld\n", *this_file_p);
    if (*this_file_p >= 100)
    {
        return -EFAULT;
    }
    write_counts = write_counts > (100 - *this_file_p) ? (100 - *this_file_p) : write_counts;
    printk(KERN_INFO "redirect write counts %ld\n", write_counts);

    int remain = copy_from_user(&jihaixuan_proc_buf[*this_file_p], user_buf, write_counts);
    if (remain)
    {
        return -EFAULT;
    }
    else
    {
        // for(int i=0;i<100;i++){
        //     printk(KERN_INFO "%d ",jihaixuan_proc_buf[i]);
        // }
        // printk("\n");
        *this_file_p += write_counts;
        return write_counts;
    }
}
loff_t jihaixuan_proc_lseek(struct file *f, loff_t offset, int seek_mode)
{
    loff_t ret;
    printk(KERN_INFO "seek mode is %d\n", seek_mode);
    switch (seek_mode)
    {
    case SEEK_CUR:
        ret = f->f_pos + offset;
        break;
    case SEEK_SET:
        ret = offset;
        break;
    case SEEK_END:
        ret = 99 + offset;
        break;
    default:
        return -EFAULT;
    }
    if (ret < 100 && ret >= 0)
    {
        f->f_pos = ret;
        return ret;
    }
    else
    {
        return -EFAULT;
    }
};
static struct proc_ops jihaixuan_proc_ops = {
    .proc_lseek = jihaixuan_proc_lseek,
    .proc_read = jihaixuan_proc_read,
    .proc_write = jihaixuan_proc_write,
    .proc_open = jihaixuan_proc_open,
    .proc_release = jihaixuan_proc_release,
};
static int __init init_function(void)
{
    printk(KERN_INFO "int init\n");
    jihaixuan_proc_buf = kmalloc(100, GFP_KERNEL);
    printk(KERN_INFO "kmalloc over\n");
    if (!jihaixuan_proc_buf)
    {
        printk(KERN_WARNING "kmalloc(100,GFP_KERNEL) failed\n");
        printk(KERN_WARNING "exit driver\n");
        printk(KERN_INFO "good bye kernel\n");
        return -EFAULT;
    }
    printk(KERN_INFO "init data\n");
    char init_data[] = "helloworld";
    for(int i =0;i<100;i++){
        *(jihaixuan_proc_buf+i)=init_data[i%10];
    } 
    printk(KERN_INFO "init data over\n");
    proc_create("jihaixuan", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, NULL, &jihaixuan_proc_ops);
    printk(KERN_INFO "welcome to my proc driver\n");
    return 0;
}
static void __exit exit_function(void)
{
    kfree(jihaixuan_proc_buf);
    remove_proc_entry("jihaixuan",NULL);
    printk(KERN_INFO "good bye kernel\n");
}

module_init(init_function);
module_exit(exit_function);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("jihaixuan");